// httpListener
// Http端口监听单元
package main

import (
	//"github.com/Azure/go-autorest/autorest/azure/cli"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"server/src/common"
	"strconv"
	"time"

	"server/src/gps"
)

var UseMap = common.BaiDuMap

// 天地图API接口
var TiandiAPIUrl = "https://api.tianditu.gov.cn/geocoder?postStr={'lon':%f,'lat':%f,'ver':1}&type=geocode&tk=%s"

// 百度地图API接口
// var BaiduAPIUrl string = "http://api.map.baidu.com/geocoder/v2/?location=%f,%f&output=json&ak=%s"
var BaiduAPIUrl string = "http://api.map.baidu.com/reverse_geocoding/v3/?output=json&coordtype=wgs84ll&location=%f,%f&ak=%s"

// Mapbox地图API接口
var MapboxAPIUrl string = "https://api.mapbox.com/geocoding/v5/mapbox.places/%f,%f.json?access_token=%s"

type HttpListener struct {
	cfg        *Config
	dbo        *DBOperator
	gpsProcess gps.GPS //用来转换坐标系
}

func ListenHttpService(cfg *Config) error {
	dbo, err := NewDBOperator(cfg.DBUrl, cfg.DBName, cfg.DBUser, cfg.DBPwd)
	if err != nil {
		return err
	}
	if cfg.UseMap != "" {
		UseMap = cfg.UseMap
	}
	if cfg.TiandiAPIUrl != "" {
		TiandiAPIUrl = cfg.TiandiAPIUrl
	}
	if cfg.BaiduAPIUrl != "" {
		BaiduAPIUrl = cfg.BaiduAPIUrl
	}
	if cfg.MapboxAPIUrl != "" {
		MapboxAPIUrl = cfg.MapboxAPIUrl
	}

	listenter := HttpListener{}
	listenter.cfg = cfg
	listenter.dbo = dbo

	zLog(nil, "开始监听Http端口:", cfg.HttpPort)
	listenter.listen(cfg.HttpPort)

	return nil
}

func (p *HttpListener) listen(port int) {
	sPort := fmt.Sprintf(":%d", port)

	http.HandleFunc("/coder", p.GeoCoder)

	//开始监听
	zLog(nil, "开始监听端口:", sPort, " HTTPS:", p.cfg.HTTPS)
	var err error
	srv := &http.Server{
		Addr:         sPort, //":443",
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 5 * time.Second,
	}
	srv.SetKeepAlivesEnabled(false)
	if p.cfg.HTTPS {
		err = srv.ListenAndServeTLS(p.cfg.CertFile, p.cfg.KeyFile)
		//err := http.ListenAndServeTLS(sPort, p.cfg.CertFile, p.cfg.KeyFile, nil)
	} else {
		err = srv.ListenAndServe()
		//err = http.ListenAndServe(sPort, nil)
	}
	if err != nil {
		zLog(nil, "HTTPS监听失败:", err)
	}
}

func (p *HttpListener) GeoCoder(w http.ResponseWriter, req *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	req.ParseForm()
	zLog(nil, "GeoCoder start req.PostForm::", req.PostForm.Encode())
	//此处接收的是原始的GPS数据，不是百度或高德的数据
	lat := req.FormValue("lat")
	lng := req.FormValue("lng")
	language := req.FormValue("lang")
	fixed := req.FormValue("fixed")

	if lat == "" || lng == "" {
		w.Write([]byte("What the fuck?"))
		return
	}

	var iFixed int
	var fLat, fLng float64
	var err error

	fLat, err = strconv.ParseFloat(lat, 32)
	if err == nil {
		fLng, err = strconv.ParseFloat(lng, 32)
	}
	if err != nil {
		w.Write([]byte("Fuck you!"))
		return
	}
	iFixed, err = strconv.Atoi(fixed)
	if err != nil {
		iFixed = 1 //默认为已纠偏的数据
	}

	res := p.getLocation(fLat, fLng, iFixed, language)
	buf, _ := json.Marshal(res)
	w.Write(buf)
}

type BaiduLocationResultT struct {
	Address string `json:"formatted_address"`
}
type BaiduLocationT struct {
	Status int                  `json:"status"`
	Error  string               `json:"message"`
	Result BaiduLocationResultT `json:"result"`
}

func (p *HttpListener) getLocation(lat, lng float64, fixed int, language string) *BaiduLocationT {
	var bl *BaiduLocationT

	//首先从数据库中查询
	sAdd := p.dbo.Search(lat, lng, fixed, language)
	if sAdd != "" {
		bl = &BaiduLocationT{Status: 0, Error: "", Result: BaiduLocationResultT{Address: sAdd}}
	} else {
		if language == "" {
			language = "cn"
		}
		if language == "cn" {
			//如果是中文，则从百度查询
			var latFixed, lngFixed float64
			if fixed == 1 {
				latFixed, lngFixed = lat, lng
			} else {
				//将原始坐标转换为百度坐标再查询
				latFixed, lngFixed = p.gpsProcess.TransformFromWGSToGCJ(lat, lng)
				latFixed, lngFixed = p.gpsProcess.BDEncrypt(latFixed, lngFixed)
			}

			bl = p.getBaiduLocation(latFixed, lngFixed)
		} else {
			//否则从Mapbox查询
			bl = p.getMapboxLocation(lat, lng)
		}
		if bl.Result.Address != "" {
			go p.dbo.Save(lat, lng, bl.Result.Address, fixed, language)
		} else if bl.Error != "" {
			fmt.Println(bl.Error)
		}
	}

	return bl
}

// 百度地图
func (p *HttpListener) getBaiduLocation(lat, lng float64) *BaiduLocationT {
	bl := BaiduLocationT{Status: -1}
	sURL := fmt.Sprintf(BaiduAPIUrl, lat, lng, p.cfg.BaiduAccessToken)
	zLog(nil, sURL)
	resp, err := p.TslGet(sURL) //client.Get(sURL)
	if err != nil {
		bl.Error = err.Error()
		return &bl
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		bl.Error = err.Error()
		return &bl
	}
	// fmt.Println(string(body))

	err = json.Unmarshal(body, &bl)
	if err != nil {
		bl.Error = err.Error()
	}

	return &bl
}

// MapboxFeatureT struct
type MapboxFeatureT struct {
	PlaceName string `json:"place_name"`
}

// MapboxLocationT struct
type MapboxLocationT struct {
	Features []MapboxFeatureT `json:"features"`
}

func (p *HttpListener) getMapboxLocation(lat, lng float64) *BaiduLocationT {
	bl := BaiduLocationT{Status: -1}

	sURL := fmt.Sprintf(MapboxAPIUrl, lng, lat, p.cfg.MapboxAccessToken)
	resp, err := p.TslGet(sURL) //http.Get(sURL)
	if err != nil {
		bl.Error = err.Error()
		return &bl
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		bl.Error = err.Error()
		return &bl
	}

	var ml MapboxLocationT
	err = json.Unmarshal(body, &ml)
	if err != nil {
		bl.Error = err.Error()
	} else {
		if len(ml.Features) > 0 {
			bl.Status = 0
			bl.Result.Address = ml.Features[0].PlaceName
		}
	}

	return &bl
}

func (p *HttpListener) TslGet(url string) (resp *http.Response, err error) {
	tranport := http.Transport{
		TLSHandshakeTimeout: 700 * time.Millisecond,
	}

	client := http.Client{Transport: &tranport}
	return client.Get(url)
}
